﻿using System;
using AdaptiveAgents.Loggers;
using AdaptiveAgents.Agents;

namespace AdaptiveAgents.Games
{
    /// <summary>
    /// Represents a cyclic order game
    /// Notice that only the deciding order on the next active player is cyclic,
    /// but the next operating agent is according to the decision
    /// </summary>
    class CyclicGame : Game
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public CyclicGame(Environment env, int numOfRounds)
            : base(env, numOfRounds)
        { }

        /// <summary>
        /// Decides which player is the next player
        /// </summary>
        /// <param name="activeDecider">the active player</param>
        /// <returns>the next active player according to the agent decision (not necessarily the real next player)</returns>
        protected override int chooseNextPerformer(int activeDecider)
        {
            return _environment.agents[activeDecider].chooseNextPerformer();
        }

       
        /// <summary>
        /// Start playing the game
        /// </summary>
        /// <param name="logger">Logger that monitors game activity</param>
        /// <returns>Average utility of the players</returns>
        public override double start(Logger logger)
        {
            int numOfAgents = _environment.agents.Count;

            //Print the game type
            logger.WriteLine(MessageType.GameDetails, numOfAgents, GameType.Cyclic.ToString());

            //Initialization of game environment
            initializeGame(logger, numOfAgents);

            _accumUtility = 0; //Total utility of all agents in group
            int nextActivePlayer;
            int activeDecider = Generator.getNextInt(numOfAgents); //First agent that decides which player performs in each round
            int activePlayer = chooseNextPerformer(activeDecider); //Choose the first agent to play
            
            //Play the game
            for (int round = 0; round < _numOfRounds; ++round)
            {
                //Create a turn record
                Turn turn = new Turn(numOfAgents);

                turn.ActiveDecider = activeDecider;
                turn.ActivePlayer = activePlayer;

                //Make a move by the active player
                makeMove(round, activePlayer, turn, ref _accumUtility);

                //Choose the next decider
                activeDecider = (activeDecider + 1) % numOfAgents;
                
                //Decide who is the next player to perform
                nextActivePlayer = chooseNextPerformer(activeDecider); 
                turn.NextActivePlayer = nextActivePlayer; //Next performer was chosen

                //Update the last observation of each player with the next player
                foreach (Agent agent in _environment.agents)
                {
                    bool actExpected = agent.updateLastObservationWithNextAgent(activePlayer, nextActivePlayer);
                    turn.addExpectedAct(actExpected);
                }
 
                //Set active player
                activePlayer = nextActivePlayer;
            
                //Log: turn data
                logger.WriteLine(MessageType.Turn, turn);
            }

            return logStatistics(logger);
        }
    }
}
